1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.collect;
16  
17  import static com.google.common.base.Preconditions.checkArgument;
18  
19  import com.google.common.annotations.GwtCompatible;
20  import com.google.common.annotations.GwtIncompatible;
21  
22  import java.io.IOException;
23  import java.io.ObjectInputStream;
24  import java.io.ObjectOutputStream;
25  import java.util.EnumMap;
26  import java.util.Iterator;
27  
28  /**
29   * Multiset implementation backed by an {@link EnumMap}.
30   * 
31   * <p>See the Guava User Guide article on <a href=
32   * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset">
33   * {@code Multiset}</a>.
34   *
35   * @author Jared Levy
36   * @since 2.0 (imported from Google Collections Library)
37   */
38  @GwtCompatible(emulated = true)
39  public final class EnumMultiset<E extends Enum<E>> extends AbstractMapBasedMultiset<E> {
40    /** Creates an empty {@code EnumMultiset}. */
41    public static <E extends Enum<E>> EnumMultiset<E> create(Class<E> type) {
42      return new EnumMultiset<E>(type);
43    }
44  
45    /**
46     * Creates a new {@code EnumMultiset} containing the specified elements.
47     *
48     * <p>This implementation is highly efficient when {@code elements} is itself a {@link
49     * Multiset}.
50     *
51     * @param elements the elements that the multiset should contain
52     * @throws IllegalArgumentException if {@code elements} is empty
53     */
54    public static <E extends Enum<E>> EnumMultiset<E> create(Iterable<E> elements) {
55      Iterator<E> iterator = elements.iterator();
56      checkArgument(iterator.hasNext(), "EnumMultiset constructor passed empty Iterable");
57      EnumMultiset<E> multiset = new EnumMultiset<E>(iterator.next().getDeclaringClass());
58      Iterables.addAll(multiset, elements);
59      return multiset;
60    }
61    
62    /**
63     * Returns a new {@code EnumMultiset} instance containing the given elements.  Unlike
64     * {@link EnumMultiset#create(Iterable)}, this method does not produce an exception on an empty
65     * iterable.
66     * 
67     * @since 14.0
68     */
69    public static <E extends Enum<E>> EnumMultiset<E> create(Iterable<E> elements, Class<E> type) {
70      EnumMultiset<E> result = create(type);
71      Iterables.addAll(result, elements);
72      return result;
73    }
74  
75    private transient Class<E> type;
76  
77    /** Creates an empty {@code EnumMultiset}. */
78    private EnumMultiset(Class<E> type) {
79      super(WellBehavedMap.wrap(new EnumMap<E, Count>(type)));
80      this.type = type;
81    }
82  
83    @GwtIncompatible("java.io.ObjectOutputStream")
84    private void writeObject(ObjectOutputStream stream) throws IOException {
85      stream.defaultWriteObject();
86      stream.writeObject(type);
87      Serialization.writeMultiset(this, stream);
88    }
89  
90    /**
91     * @serialData the {@code Class<E>} for the enum type, the number of distinct
92     *             elements, the first element, its count, the second element, its
93     *             count, and so on
94     */
95    @GwtIncompatible("java.io.ObjectInputStream")
96    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
97      stream.defaultReadObject();
98      @SuppressWarnings("unchecked") // reading data stored by writeObject
99      Class<E> localType = (Class<E>) stream.readObject();
100     type = localType;
101     setBackingMap(WellBehavedMap.wrap(new EnumMap<E, Count>(type)));
102     Serialization.populateMultiset(this, stream);
103   }
104 
105   @GwtIncompatible("Not needed in emulated source")
106   private static final long serialVersionUID = 0;
107 }